route.js 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. import { listMonths } from "@/lib/storage";
  2. import { getSession } from "@/lib/auth/session";
  3. import { canAccessBranch } from "@/lib/auth/permissions";
  4. import {
  5. withErrorHandling,
  6. json,
  7. badRequest,
  8. unauthorized,
  9. forbidden,
  10. } from "@/lib/api/errors";
  11. import { mapStorageReadError } from "@/lib/api/storageErrors";
  12. /**
  13. * Next.js Route Handler caching configuration (RHL-006):
  14. *
  15. * We force this route to execute dynamically on every request.
  16. *
  17. * Reasons:
  18. * - NAS contents can change at any time (new scans).
  19. * - Auth/RBAC-protected responses must not be cached/shared across users.
  20. * - We rely on a small storage-layer TTL micro-cache instead of Next route caching.
  21. */
  22. export const dynamic = "force-dynamic";
  23. /**
  24. * GET /api/branches/[branch]/[year]/months
  25. *
  26. * Happy-path response must remain unchanged:
  27. * { "branch": "NL01", "year": "2024", "months": ["10", ...] }
  28. */
  29. export const GET = withErrorHandling(
  30. async function GET(request, ctx) {
  31. const session = await getSession();
  32. if (!session) {
  33. throw unauthorized("AUTH_UNAUTHENTICATED", "Unauthorized");
  34. }
  35. const { branch, year } = await ctx.params;
  36. // Validate required route params early.
  37. const missing = [];
  38. if (!branch) missing.push("branch");
  39. if (!year) missing.push("year");
  40. if (missing.length > 0) {
  41. throw badRequest(
  42. "VALIDATION_MISSING_PARAM",
  43. "Missing required route parameter(s)",
  44. { params: missing }
  45. );
  46. }
  47. if (!canAccessBranch(session, branch)) {
  48. throw forbidden("AUTH_FORBIDDEN_BRANCH", "Forbidden");
  49. }
  50. try {
  51. const months = await listMonths(branch, year);
  52. return json({ branch, year, months }, 200);
  53. } catch (err) {
  54. throw await mapStorageReadError(err, { details: { branch, year } });
  55. }
  56. },
  57. { logPrefix: "[api/branches/[branch]/[year]/months]" }
  58. );